#ifndef PushButton_h
#define PushButton_h

class PushButton
{
    public:
    
        // The polarity of the push button push
        // as it related to the input pin
        enum Polarity
        {
            ACTIVE_LOW = 0, // Input read LOW when push button is pushed
            ACTIVE_HIGH = 1 // Input reads HIGH when push button is pushed
        };

        // The activity which has been detected on the push button
        enum Activity
        {
            NO_ACTIVITY,    // No activity
            SINGLE_PUSH,    // Single push (i.e. push and release)
            CONSTANT_PUSH,  // Constant push (i.e. held pushed)
            DOUBLE_PUSH,    // Double push (i.e. push, release, push and release)
            CONSTANT_PUSH2  // Constant (secondary) push (i.e. held pushed after initial push and release)
        };
        
        // States of the internal input pin monitor
        enum State
        {
            IDLE,
            DEBOUNCE_AFTER_IDLE,
            WAIT_AFTER_INITIAL_HIGH_TRANSITION,
            PUSHED_CONSTANTLY,
            DEBOUNCE_AFTER_HIGH,
            WAIT_AFTER_INITIAL_LOW_TRANSITION,
            DEBOUNCE_AFTER_LOW,
            WAIT_AFTER_SECONDARY_HIGH_TRANSITION,
            PUSHED_CONSTANTLY_SECONDARY
        };
    
        PushButton(int pinNumber);
        PushButton(
            int pinNumber,
            int polarity);

        int Service(void);
        
        typedef unsigned char (*DigitalReadCallback) (unsigned int pinNumber, void* object);

    private:

        // The pin to be monitored (i.e. the pin that
        // the push button is connected to)
        int mPinNumber;
        
        // Push button push polarity
        int mPolarity;
        
        // Current state of the internal input pin monitor
        int mState;

        // Minimum time (in ms) to wait for debouncing of input signal
        unsigned int mDebounceDelayMsMin;
        
        // Minimum time (in ms) to wait until a push of a button is
        // considered a constant push (push button constantly held active)
        unsigned int mConstantActiveDelayMsMin;
        
        // Maximum amount of time (in ms) to wait for a secondary push
        // of a button as part of a double push (i.e. push-release-push)
        unsigned int mDoubleActiveDelayMsMax;

        unsigned long mCounterMsLast;
        
        // Returns true if a certain delay from the last time 'mCounterMsLast'
        // has been set expired, otherwise false. Upon expiration (i.e. return
        // of true) it resets the counter to the current count.
        bool CounterExpired(unsigned long expirationDelayMs);
        
        // Resets the delay expiration counter
        void CounterReset(unsigned long resetValue = 0);
        
        // Dispatcher for digital read requests
        unsigned char DigitalRead(unsigned int pinNumber);
        
        typedef struct OnDigitalReadCallback
        {
        	OnDigitalReadCallback(void) :
        		member(0x0),
        		object(0x0) {}
        		
        	DigitalReadCallback member;
        	void*               object;
        } OnDigitalReadCallback;
        
        OnDigitalReadCallback mOnDigitalReadCallback;
        
    public:
    
    	void OnDigitalRead(DigitalReadCallback callback, void* object)
        {
        	mOnDigitalReadCallback.member = callback;
        	mOnDigitalReadCallback.object = object;
        }
};

#endif
